package util

import (
	"awesomeProject/app/response"
	"awesomeProject/global"
	"context"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/shopspring/decimal"
	"go.mongodb.org/mongo-driver/bson"
	"io"
	"log"
	"math/rand"
	"net/http"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"awesomeProject/app/model"
	"awesomeProject/crpyt"
)

type RespData struct{
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// MD5V md5加密
func MD5V(str []byte) string {
	h := md5.New()
	h.Write(str)
	return hex.EncodeToString(h.Sum(nil))
}

// AES加密
func AESEncrypt(str string, key []byte, iv []byte) (string, error) {
	plaintext := []byte(str)
	aesCprypt := crpyt.AesCrypt{}
	var err error
	var encryptedText string
	ciphertext, err := aesCprypt.AesCBCEncrypt(plaintext, key, iv)
	if err != nil {
		return encryptedText, err
	}
	//取得加密base64后的密码
	encryptedText = base64.StdEncoding.EncodeToString(ciphertext)
	return encryptedText, nil
}

// AES解密
func AESDecrypt(str string, key []byte, iv []byte) (string, error) {
	var err error
	var decryptedText string
	ciphertext, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return decryptedText, err
	}
	ciphertext = []byte(ciphertext)
	aesCprypt := crpyt.AesCrypt{}
	ciphertext, err = aesCprypt.AesCBCDecrypt(ciphertext, key, iv)
	if err != nil {
		return decryptedText, err
	}
	//取得解密明文
	decryptedText = string(ciphertext)
	return decryptedText, nil
}

// Fordeal加密 (适用于密码加密)
func FordealEncrypt(str string) (string, error) {
	key := []byte("fordeal6merchant")
	iv := []byte("abcdefghijk1mnop")
	encryptedText, err := AESEncrypt(str, key, iv)
	return encryptedText, err
}

// Fordeal解密 (适用于密码解密)
func FordealDecrypt(str string) (string, error) {
	key := []byte("fordeal6merchant")
	iv := []byte("abcdefghijk1mnop")
	decryptedText, err := AESDecrypt(str, key, iv)
	return decryptedText, err
}

// RandomStr Random 随机字符串生成
func RandomStr(length int, char string) string {
	bytes := []byte(char)
	var result []byte
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < length; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}

	return string(result)
}

// CurrentLogin 当前登录用户
func CurrentLogin(ctx *gin.Context) (u model.User) {
	cacheData, _ := ctx.Get("user")
	return cacheData.(model.User)
}

// CurrentLoginUid 当前登录用户uid
func CurrentLoginUid(ctx *gin.Context) uint {
	user := CurrentLogin(ctx)
	return user.ID
}

// IncreasePercent 增长百分比
// a 往期，b当期
func IncreasePercent(a, b interface{}) decimal.Decimal {
	x := GetInterfaceToDecimal(a)
	y := GetInterfaceToDecimal(b)
	zero := decimal.NewFromInt(0)
	if x.Equal(decimal.NewFromInt(0)) && y.GreaterThan(zero) {
		return decimal.NewFromInt(1)
	}
	if y.Equal(zero) {
		return zero
	}

	return (y.Sub(x)).Div(x)
}

// 百分比
func RatePercent(a, b interface{}) decimal.Decimal {
	x := GetInterfaceToDecimal(a)
	y := GetInterfaceToDecimal(b)
	zero := decimal.NewFromInt(0)
	//hundred := decimal.NewFromInt(100)
	if y.Equal(decimal.NewFromInt(0)) {
		return zero
	}

	return (x).Div(y)
}

func Div(a, b interface{}) decimal.Decimal {
	x := GetInterfaceToDecimal(a)
	y := GetInterfaceToDecimal(b)
	zero := decimal.NewFromInt(0)
	if y.Equal(decimal.NewFromInt(0)) {
		return zero
	}

	return x.Div(y)
}

func GetInterfaceToInt(t1 interface{}) int {
	var t2 int
	switch t1.(type) {
	case uint:
		t2 = int(t1.(uint))
		break
	case int8:
		t2 = int(t1.(int8))
		break
	case uint8:
		t2 = int(t1.(uint8))
		break
	case int16:
		t2 = int(t1.(int16))
		break
	case uint16:
		t2 = int(t1.(uint16))
		break
	case int32:
		t2 = int(t1.(int32))
		break
	case uint32:
		t2 = int(t1.(uint32))
		break
	case int64:
		t2 = int(t1.(int64))
		break
	case uint64:
		t2 = int(t1.(uint64))
		break
	case float32:
		t2 = int(t1.(float32))
		break
	case float64:
		t2 = int(t1.(float64))
		break
	case string:
		t2, _ = strconv.Atoi(t1.(string))
		if t2 == 0 && len(t1.(string)) > 0 {
			f, _ := strconv.ParseFloat(t1.(string), 64)
			t2 = int(f)
		}
		break
	case nil:
		t2 = 0
		break
	case json.Number:
		t3, _ := t1.(json.Number).Int64()
		t2 = int(t3)
		break
	default:
		t2 = t1.(int)
		break
	}
	return t2
}

func GetInterfaceToDecimal(t1 interface{}) decimal.Decimal {
	var t2 decimal.Decimal
	switch t1.(type) {
	case int:
		t2 = decimal.NewFromInt(int64(t1.(int)))
		break
	case uint:
		t2 = decimal.NewFromInt(int64(t1.(uint)))
		break
	case int8:
		t2 = decimal.NewFromInt(int64(t1.(int8)))
		break
	case uint8:
		t2 = decimal.NewFromInt(int64(t1.(uint8)))
		break
	case int16:
		t2 = decimal.NewFromInt(int64(t1.(int16)))
		break
	case uint16:
		t2 = decimal.NewFromInt(int64(t1.(uint16)))
		break
	case int32:
		t2 = decimal.NewFromInt(int64(t1.(int32)))
		break
	case uint32:
		t2 = decimal.NewFromInt(int64(t1.(uint32)))
		break
	case int64:
		t2 = decimal.NewFromInt(t1.(int64))
		break
	case uint64:
		t2 = decimal.NewFromInt(int64(t1.(uint64)))
		break
	case float32:
		t2 = decimal.NewFromFloat(float64(t1.(float32)))
		break
	case float64:
		t2 = decimal.NewFromFloat(t1.(float64))
		break
	case string:
		t2, _ = decimal.NewFromString(t1.(string))
		break
	case nil:
		t2 = decimal.NewFromInt(0)
		break
	case json.Number:
		t3, _ := t1.(json.Number).Int64()
		t2 = decimal.NewFromInt(t3)
		break
	default:
		t2 = t1.(decimal.Decimal)
		break
	}
	return t2
}

func GetMonthStartAndEnd(yearInt int, monthInt int) map[string]time.Time {
	// 数字月份必须前置补零
	monthStr := fmt.Sprintf("%d", monthInt)
	if len(monthStr) == 1 {
		monthStr = fmt.Sprintf("0%s", monthStr)
	}

	timeLayout := "2006-01-02 15:04:05"
	loc, _ := time.LoadLocation("Local")
	theTime, _ := time.ParseInLocation(timeLayout, fmt.Sprintf("%d-%s-01 00:00:00", yearInt, monthStr), loc)
	newMonth := theTime.Month()

	t1 := time.Date(yearInt, newMonth, 1, 0, 0, 0, 0, time.Local)
	t2 := time.Date(yearInt, newMonth+1, 0, 23, 59, 59, 9999999, time.Local)
	result := map[string]time.Time{"start": t1, "end": t2}
	return result
}

func UnixToStr(timeUnix int64, layout string) string {
	timeStr := time.Unix(timeUnix, 0).Format(layout)
	return timeStr
}

func Contains(slice []uint, element uint) bool {
	for _, i := range slice {
		if i == element {
			return true
		}
	}
	return false
}

func GetRespData(variable response.Resp) RespData {
	var data RespData
	err := json.Unmarshal([]byte(variable.ToString()), &data)
	if err != nil {
		log.Fatalf("failed unmarshalling: %v", err)
	}
	return data
}

func GetTableDataID(collname string) uint {
	table := global.MongoDB.Collection("ids") // 指定表名为ids表
	var result struct {
		Name   string `json:"name" bson:"name"`
		NextID uint   `json:"next_id" bson:"next_id"`
	}
	table.FindOneAndUpdate(
		context.TODO(),
		bson.M{"name": collname},
		bson.M{"$inc": bson.M{"next_id": 1}}).Decode(&result)
	return result.NextID
}

func IsNumeric(variable string) bool {
	match, _ := regexp.MatchString("^[0-9]+$", variable)
	return match
}

func GetClientIP() string {
	resp, err := http.Get("http://httpbin.org/ip")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	type Data struct {
		Origin string `json:"origin"`
	}

	var data Data
	err = json.Unmarshal(body, &data)
	if err != nil {
		log.Fatal(err)
	}

	ip := data.Origin
	return ip
}

// 获取ip位置信息
func GetIpLocaton(ip string) string {
	//http://ip-api.com/json/123.123.123.123?lang=zh-CN
	resp, err := http.Get("https://searchplugin.csdn.net/api/v1/ip/get?ip="+ ip +"")
	//返回： {"code":200,"msg":"success","data":{"address":"中国 北京 北京 联通","ip":"123.123.123.123"}}
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	type Data struct {
		Address string `json:"address"`
		Ip      string `json:"ip"`
	}
	type JsonResult struct {
		Code int `json:"code"`
		Msg  string `json:"msg"`
		Data Data
	}


	var jsonData JsonResult
	err = json.Unmarshal(body, &jsonData)
	if err != nil {
		log.Fatal(err)
	}

	location := jsonData.Data.Address
	return location
}

func GetIpData(strs string) []string {
	strs = strings.Trim(strs, " ")
	strs = strings.ReplaceAll(strs, "　", "|")
	strs = strings.ReplaceAll(strs, " ", "|")
	strs = strings.ReplaceAll(strs, "\n", "|")
	strs = strings.ReplaceAll(strs, "\r", "|")
	strs = strings.ReplaceAll(strs, "\t", "|")
	strs = strings.ReplaceAll(strs, ",", "|")
	strs = strings.ReplaceAll(strs, ";", "|")
	strs = strings.ReplaceAll(strs, "|", "|")
	strs = strings.TrimSpace(strs)

	arr := strings.Split(strs, "|")

	var ips []string
	for _, val := range arr {
		if val != "" {
			ips = append(ips, val)
		}
	}
	return ips
}

// 判断字符串数组中是否存在某个值
func IsExistsValue(arr []string, val string) bool {
	var set map[string]struct{}

	set = make(map[string]struct{})
	for _, value := range arr{
		set[value] = struct{}{}
	}
	result := false
	if _, ok := set[val];ok {
		result = true
	}
	return result
}

// 判断uint数组中是否存在某个值
func IsExistsUint(arr []uint, val uint) bool {
	//定义map
	m := make(map[uint]bool)
	//将数组中的值逐一存入map中
	for i := 0;i < len(arr);i++ {
		m[arr[i]] = true
	}
	//判断目标值是否在map中
	if m[val] {
		return true
	} else {
		return false
	}
}

// 求两个uint数组切片的交集
func UnionStringArrays(a []string, b []string) []string {
	a = append(a, b...)
	return a
}

// 求两个uint数组切片的交集
func IntersectUIntArray(a []uint, b []uint) []uint {
	var inter []uint
	mp := make(map[uint]bool)

	for _, s := range a {
		if _, ok := mp[s]; !ok {
			mp[s] = true
		}
	}
	for _, s := range b {
		if _, ok := mp[s]; ok {
			inter = append(inter, s)
		}
	}

	return inter
}

// 求两个uint数组切片的差集
func DiffUintArray(a []uint, b []uint) []uint {
	var diffArray []uint
	temp := map[uint]struct{}{}

	for _, val := range b {
		if _, ok := temp[val]; !ok {
			temp[val] = struct{}{}
		}
	}

	for _, val := range a {
		if _, ok := temp[val]; !ok {
			diffArray = append(diffArray, val)
		}
	}

	return diffArray
}

// uint数组切片去重实现
func ArrayUintUnique(arr []uint) []uint {
	result := make([]uint, 0, len(arr))
	temp := map[uint]struct{}{}
	for i := 0; i < len(arr); i++ {
		if _, ok := temp[arr[i]]; ok != true {
			temp[arr[i]] = struct{}{}
			result = append(result, arr[i])
		}
	}
	return result
}

func ArrrayUintJoin(arr []uint, sep string) string {
	var result string
	for i := 0; i < len(arr); i++ {
		result += strconv.Itoa(int(arr[i])) + sep
	}
	result = strings.TrimRight(result, sep)
	return result
}

func UintsToStr(arr []uint, sep string) string {
	var result string
	for i := 0; i < len(arr); i++ {
		result += strconv.Itoa(int(arr[i])) + sep
	}
	result = strings.TrimRight(result, sep)
	return result
}

func StrToUints(str string, sep string) []uint {
	arrUint := []uint{}
	arrStr := strings.Split(str, sep)
	for _, v := range arrStr {
		i, _ := strconv.Atoi(v)
		arrUint = append(arrUint, uint(i))
	}
	return arrUint
}

func GetUserCompanyIds(user model.User) []uint {
	CompanyIds := []uint{}
	if user.CompanyId != 0 {
		CompanyIds = append(CompanyIds, user.CompanyId)
	}
	if user.ExCompanyIds != "" {
		exCompanyIds := StrToUints(user.ExCompanyIds, ",")
		for _, cid := range exCompanyIds {
			if cid != 0 {
				CompanyIds = append(CompanyIds, cid)
			}
		}
	}
	return CompanyIds
}

func AddLoginlog(username string, info string, clientIp string, ipLocation string, userAgent string) error {
	// 记录登录日志
	insertData := model.Loginlog{
		Username:   username,
		Info:       info,
		Ip:         clientIp,
		Location:   ipLocation,
		UserAgent:  userAgent,
		CreateTime: uint(time.Now().Unix()),
	}
	err := global.Gorm.Model(&model.Loginlog{}).Create(&insertData).Error
	return err
}

//binding type interface 要修改的结构体
//value type interace 有数据的结构体
func StructAssign(binding interface{}, value interface{}) {
	dataType := reflect.TypeOf(value)
	var temp map[string]interface{}
	var temp2 *map[string]interface{}
	// 类型： map[string]interface{}
	if dataType == reflect.TypeOf(temp) || dataType == reflect.TypeOf(temp2) {
		o, _ := json.Marshal(value)
		//tt := map[string]interface {}{}
		//_ = json.Unmarshal(o, &tt)
		//binding = reflect.ValueOf(tt)
		_ = json.Unmarshal(o, &binding)
	} else {
		bVal := reflect.ValueOf(binding).Elem() //获取reflect.Type类型
		vVal := reflect.ValueOf(value).Elem()   //获取reflect.Type类型
		vTypeOfT := vVal.Type()
		for i := 0; i < vVal.NumField(); i++ {
			// 在要修改的结构体中查询有数据结构体中相同属性的字段，有则修改其值
			name := vTypeOfT.Field(i).Name
			if ok := bVal.FieldByName(name).IsValid(); ok {
				//bVal.FieldByName(name).Set(reflect.ValueOf(vVal.Field(i).Interface()))
				// 检查是否需要类型转换

				structType := bVal.FieldByName(name).Type()
				dataType := reflect.TypeOf(vVal.Field(i).Interface())

				//fmt.Println("-----------")
				//fmt.Println(bVal.FieldByName(name))
				//fmt.Println(name)
				//fmt.Println(structType)
				//fmt.Println(dataType)
				//fmt.Println(vVal.Field(i).Interface())
				//fmt.Println(dataType == reflect.TypeOf(temp))
				//fmt.Println(dataType.ConvertibleTo(structType))
				//fmt.Println(reflect.TypeOf(structType))
				//fmt.Println("-----------")

				vv := reflect.ValueOf(vVal.Field(i).Interface())
				if structType == dataType || reflect.ValueOf(bVal.FieldByName(name)).Kind() == reflect.Ptr {
					bVal.FieldByName(name).Set(vv)
				} else {
					//// 转换类型
					if dataType.ConvertibleTo(structType) {
						bVal.FieldByName(name).Set(vv.Convert(structType))
					} else {
						// 类型： map[string]interface{}
						if dataType == reflect.TypeOf(temp) {
							//bVal.FieldByName(name).Set(vv)
							tt := map[string]interface {}{}
							o, _ := json.Marshal(vVal.Field(i).Interface())
							_ = json.Unmarshal(o, &tt)
							bVal.FieldByName(name).Set(reflect.ValueOf(tt))
						} else {
							bVal.FieldByName(name).Set(vv)
						}
					}
				}
			}
		}
	}
}

// 将 interface{} 转换为string, int等
func ConvertToValue(variable interface{}) interface{} {
	// 获取类型信息
	typeInfo := reflect.TypeOf(variable)

	if typeInfo == reflect.TypeOf(0) || typeInfo == reflect.TypeOf("") {
		return fmt.Sprintf("%v", variable)
	}

	// 如果类型是 int 或 string，可以直接转换
	if _, ok := variable.(uint); ok {
		return fmt.Sprintf("%v", variable)
	} else if _, ok := variable.(int); ok {
		return fmt.Sprintf("%v", variable)
	} else if _, ok := variable.(string); ok {
		return fmt.Sprintf("%v", variable)
	}

	// 否则，需要获取值并进行转换
	value := reflect.ValueOf(variable)
	switch value.Kind() {
	case reflect.Int8:
	case reflect.Int16:
	case reflect.Int32:
	case reflect.Int64:
	case reflect.Int:
		//return int(value.Int())
		//return value.Interface().(int)
		return fmt.Sprintf("%v", value.Int())
	case reflect.Float32:
	case reflect.Float64:
		return fmt.Sprintf("%v", value.Float())
	case reflect.String:
		return value.String()
	default:
		return value
	}

	switch variable.(type){
	case int:
		return int(value.Int())
	case string:
		return value.String()
	default:
		return value
	}

	return value
}

// 将 interface{} 转换为string
func ConvertToString(variable interface{}) string {
	value := ConvertToValue(variable)
	ss := value.(string)
	return ss
}

// 将 interface{} 转换为int
func ConvertToInt(variable interface{}) int {
	value := ConvertToValue(variable)
	ss := value.(int)
	return ss
}
